﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using KC.DataAccess.MV6;

namespace KC.Business.Blog
{
	public static class Utils
	{
		public enum RequestType { Invalid, None, Post, Archives, Categories, Tags }

		/// <summary>Given a request, detects the request type</summary>
		/// <param name="Request">Request to validate</param>
		/// <returns>RequestType enum representing the type of request</returns>
		public static RequestType DetectRequestType(string Request, out Dictionary<string,string> values)
		{
			values = new Dictionary<string,string>();
			if (string.IsNullOrEmpty(Request)) return RequestType.Invalid;
			Request = Request.ToLower();
			if (string.IsNullOrEmpty(Request)) throw new ArgumentNullException("Request");
			System.Collections.Specialized.NameValueCollection QueryString = System.Web.HttpUtility.ParseQueryString(Request.Replace("?", "?randomxx-8f8d989af&"));
			if (QueryString == null) return RequestType.None;

			// Detect Post
			if (Request.Contains("post.aspx") || QueryString["id"] != null || QueryString["title"] != null)
			{
				// Unfortunately, I've used a bunch of different formats over the years... Therefore...
				if (QueryString["id"] != null && !string.IsNullOrEmpty(QueryString["id"])) values.Add("id", QueryString["id"]);
				else if (QueryString["pid"] != null && !string.IsNullOrEmpty(QueryString["pid"])) values.Add("id", QueryString["pid"]);
				else if (QueryString["postid"] != null && !string.IsNullOrEmpty(QueryString["postid"])) values.Add("id", QueryString["postid"]);
				else if (QueryString["post"] != null && !string.IsNullOrEmpty(QueryString["post"])) values.Add("id", QueryString["post"]);
				else if (QueryString["title"] != null && !string.IsNullOrEmpty(QueryString["title"])) values.Add("title", QueryString["title"]);
				
				int PostID;
				if (values.ContainsKey("id") && int.TryParse(values["id"], out PostID)) return RequestType.Post;
				else if (values.ContainsKey("title")) return RequestType.Post;
				else return RequestType.Invalid;
			}
			
			// Detect Archives
			if (Request.Contains("archives.aspx") || Request.Contains("maxarch") || Request.Contains("minarch"))
			{
				DateTime min=DateTime.MinValue, max = DateTime.MaxValue;
				if (QueryString["minarch"] != null && DateTime.TryParse(QueryString["minarch"], out min)) values.Add("minarch",min.ToString("MM/dd/yyyy"));
				if (QueryString["maxarch"] != null && DateTime.TryParse(QueryString["maxarch"], out max)) values.Add("maxarch",min.ToString("MM/dd/yyyy"));
				if (min > DateTime.MinValue || max < DateTime.MaxValue) return RequestType.Archives;
				else return RequestType.Invalid;
			}

			// Detect Categories
			if (Request.Contains("categories.aspx"))
			{
				int id;
				if (QueryString["id"] != null && int.TryParse(QueryString["id"], out id)) values.Add("id", id.ToString());
				if (QueryString["cid"] != null && int.TryParse(QueryString["cid"], out id)) values.Add("cid", id.ToString());
				if (QueryString["catid"] != null && int.TryParse(QueryString["catid"], out id)) values.Add("catid", id.ToString());
				return RequestType.Categories;
			}

			// Detect Tags
			if (Request.Contains("tags.aspx"))
			{
				int id;
				if (QueryString["id"] != null && int.TryParse(QueryString["id"], out id)) values.Add("id", id.ToString());
				if (QueryString["tid"] != null && int.TryParse(QueryString["tid"], out id)) values.Add("tid", id.ToString());
				if (QueryString["tagid"] != null && int.TryParse(QueryString["tagid"], out id)) values.Add("tagid", id.ToString());
				return RequestType.Tags;
			}

			// Default: None
			return RequestType.None;
		}

		/// <summary>Updates tags on all Posts.</summary>
		public static int UpdateAllTags()
		{
			MV6DataContext dc = new MV6DataContext();
			int numSucceeded = 0;
			foreach (Post p in dc.Posts)
				try
				{
					UpdateTags(p, ref dc);
					numSucceeded++;
				}
				catch (Exception ex)
				{
					KC.Logging.Logger.Write("KC Architecture", ex, Logging.Logger.Severity.Error);
				}
			dc.Dispose();
			return numSucceeded;
		}
		/// <summary>Updates tags on a Post.</summary>
		/// <param name="p">Post to update tags on</param>
		/// <param name="dc">DataContext</param>
		public static void UpdateTags(Post p, ref MV6DataContext dc)
		{
			if (dc == null)
				throw new ArgumentNullException("dc", "cannot connect to a null data context");
			if (p == null)
				throw new ArgumentNullException("p", "Cannot update tags on a null Post");

			dc.TagToPosts.DeleteAllOnSubmit(p.TagToPosts);
			dc.SubmitChanges();
			string lowerbody = p.Body.ToLower(), lowertitle = p.Title.ToLower();
			foreach (Tag t in dc.Tags)
				if (lowerbody.Contains(t.Title.ToLower()) || lowertitle.Contains(t.Title.ToLower()))
				{
					TagToPost ttp = new TagToPost();
					ttp.PID = p.ID;
					ttp.TID = t.ID;
					dc.TagToPosts.InsertOnSubmit(ttp);
				}
			dc.SubmitChanges();
		}

		/// <summary>Returns the name of a thumbnail file associated with an image.</summary>
		/// <param name="filename">Fully qualified path and filename of image file to get thumbnail for</param>
		/// <returns>Fully qualified path and filename to associated thumbnail file, whether it exists or not</returns>
		public static string MapThumbnail(string filename)
		{
			const string ThumbSuffix = "_sm";

			if (string.IsNullOrEmpty(filename)) throw new ArgumentNullException("filename");
			if (!System.IO.File.Exists(filename)) throw new ArgumentOutOfRangeException("filename", "Filename does not exist: " + filename);
			if (!filename.Contains("\\")) throw new ArgumentOutOfRangeException("filename", "Filename must be a fully qualified path to file. Unacceptable: " + filename);

			string Path = filename.Substring(0, filename.LastIndexOf("\\"));
			string FilenameAbbr = filename.Substring(filename.LastIndexOf("\\")+1);

			if (!FilenameAbbr.Contains('.')) throw new ArgumentException("Filename must contain an extension to create a Thumbnail or find an existing one", "filename");
			string extension = FilenameAbbr.Substring(FilenameAbbr.LastIndexOf('.')+1);
			string ThumbFile = Path + "\\" + FilenameAbbr.Substring(0, FilenameAbbr.LastIndexOf(".")) + ThumbSuffix + ".jpg";
			string[] PotentialHits = System.IO.Directory.GetFiles(Path, FilenameAbbr.Substring(0, FilenameAbbr.LastIndexOf(".")) + ThumbSuffix + ".*");
			if (PotentialHits.Length > 0) return PotentialHits[0];
			else return ThumbFile;
		}

		/// <summary>Creates a thumbnail of a given image.</summary>
		/// <param name="inFile">Fully qualified path to file to create a thumbnail of</param>
		/// <param name="outFile">Fully qualified path to created thumbnail</param>
		/// <param name="x">Width of thumbnail</param>
		/// <returns>flag; result = is success</returns>
		public static bool CreateThumbnail(string inFile, string outFile, int x)
		{
			// Validation - assume 16x16 icon is smallest useful size. Smaller than that is just not going to do us any good anyway. I consider that an "Exceptional" case.
			if (string.IsNullOrEmpty(inFile)) throw new ArgumentNullException("inFile");
			if (string.IsNullOrEmpty(outFile)) throw new ArgumentNullException("outFile");
			if (x < 16) throw new ArgumentOutOfRangeException("x");
			if (!File.Exists(inFile)) throw new ArgumentOutOfRangeException("inFile", "File does not exist: " + inFile);

			// Mathematically determine Y dimension
			int y;
			using (Image img = Image.FromFile(inFile)) 
				y = (int)((double)img.Height * ((double)x / (double)img.Width));
			
			// All this crap could have easily been Image.Save(filename, x, y)... but nooooo....
			using (Bitmap bmp = new Bitmap(inFile)) 
				using (Bitmap thumb = new Bitmap((Image)bmp, new Size(x, y))) 
					using (Graphics g = Graphics.FromImage(thumb)) {
						g.SmoothingMode = SmoothingMode.HighQuality;
						g.InterpolationMode = InterpolationMode.High;
						g.CompositingQuality = CompositingQuality.HighQuality;
						ImageCodecInfo codec = ImageCodecInfo.GetImageEncoders()[1];
						EncoderParameters ep2 = new EncoderParameters(1);
						ep2.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
						g.DrawImage(bmp, new Rectangle(0,0,thumb.Width, thumb.Height));
						try {
							thumb.Save(outFile, codec, ep2);
							return true; }
						catch { return false; } }
		}
	}
}
